package org.manon.json.meta;

import java.lang.reflect.Method;
import java.lang.reflect.TypeVariable;
import java.sql.Timestamp;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.Map;

import org.manon.json.annotation.JSon;
import org.manon.json.annotation.JSonProperty;
import org.manon.json.util.ClassUtils;

/**
 * @author <a href="mailto:jacques.militello@gmail.com">Jacques Militello</a>
 */
public final class MetaPropertyFactory {

	private MetaPropertyFactory() {}
	

	public static MetaProperty newInstance(JSonProperty jsonProperty,String property, Method method) {
		
		if (Collection.class.isAssignableFrom(method.getReturnType())) {
			return new MetaPropertyCollection(property,(jsonProperty == null) ? property : jsonProperty.value(), method);
		}

		if (Map.class.isAssignableFrom(method.getReturnType())) {
			return new MetaPropertyMap(property,(jsonProperty == null) ? property : jsonProperty.value(), method);
		}
		
		// check if it's an association
		final JSon json = method.getReturnType().getAnnotation(JSon.class);
		if (json != null) {
			return new MetaPropertyAssociation(property,(jsonProperty == null) ? property : jsonProperty.value(), method);
		}
		
		final Class<?> returnClass = method.getReturnType();
		
		if (method.getGenericReturnType() != null && method.getGenericReturnType() instanceof TypeVariable) {
			return new MetaPropertyGeneric(property,(jsonProperty == null) ? property : jsonProperty.value(), method);
		}
		
		if (boolean.class.isAssignableFrom(returnClass) || Boolean.class.isAssignableFrom(returnClass) 
				|| byte.class.isAssignableFrom(returnClass) || Byte.class.isAssignableFrom(returnClass)
				|| short.class.isAssignableFrom(returnClass) || Short.class.isAssignableFrom(returnClass)
				|| int.class.isAssignableFrom(returnClass) || Integer.class.isAssignableFrom(returnClass) 
				|| long.class.isAssignableFrom(returnClass) || Long.class.isAssignableFrom(returnClass) 
				|| float.class.isAssignableFrom(returnClass) || Float.class.isAssignableFrom(returnClass)
				|| double.class.isAssignableFrom(returnClass) || Double.class.isAssignableFrom(returnClass)
				|| String.class.isAssignableFrom(returnClass) || Date.class.isAssignableFrom(returnClass) 
				|| Calendar.class.isAssignableFrom(returnClass) || Timestamp.class.isAssignableFrom(returnClass)
				|| Enum.class.isAssignableFrom(returnClass)) {
		
			return new MetaPropertySingleValue(property,(jsonProperty == null) ? property : jsonProperty.value(), method);
		}
		
		//check if it's an override getter ?
		
		if (check(method.getDeclaringClass().getSuperclass(),ClassUtils.getGetter(property))) {
			return null;
		}
			
		throw new IllegalStateException("Not supported : [" + method.getReturnType() + "] -> [" + method + "]");
		
	}


	private static boolean check(Class<?> clazz,String methodName) {
		
		if (clazz.equals(Object.class)) {
			return false;
		}
		
		for (Method method : clazz.getDeclaredMethods()) {
			if (methodName.equals(method.getName())) {
				return true;
			}
		}
		
		return check(clazz.getSuperclass(),methodName);
	}
	
}